﻿namespace OpenComposite.Base.Collections
{
	using System;
	using System.ComponentModel;
	using System.Reflection;

	public class PropertyComparer<T> : System.Collections.Generic.IComparer<T>
	{
		#region Initialization

		public PropertyComparer(PropertyDescriptor property, ListSortDirection direction)
		{
			_property = property;
			_direction = direction;
		}

		#endregion Initialization

		#region Public Members

		#region Public Methods

		public int Compare(T xWord, T yWord)
		{
			// Get property values
			object xValue = GetPropertyValue(xWord, _property.Name);
			object yValue = GetPropertyValue(yWord, _property.Name);

			// Determine sort order
			if ( _direction == ListSortDirection.Ascending ) {
				return CompareAscending(xValue, yValue);
			} else {
				return CompareDescending(xValue, yValue);
			}
		}

		public bool Equals(T xWord, T yWord)
		{
			return xWord.Equals(yWord);
		}

		public int GetHashCode(T obj)
		{
			return obj.GetHashCode();
		}

		#endregion Public Methods

		#endregion Public Members

		#region Private Members

		#region Private Fields

		private ListSortDirection _direction;

		// The following code contains code implemented by Rockford Lhotka:
		// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp
		private PropertyDescriptor _property;

		#endregion Private Fields

		#region Private Methods

		// Compare two property values of any type
		private int CompareAscending(object xValue, object yValue)
		{
			int result;

			if ( xValue == null && yValue == null ) {
				// both values are null
				result = 0;
			} else if ( xValue == null ) {
				// xValue is null
				result = -1;
			} else if ( yValue == null ) {
				// yValue is null
				result = 1;
			} else if ( xValue is IComparable ) {
				// If values implement IComparer
				result = ( (IComparable)xValue ).CompareTo(yValue);
			} else if ( xValue.Equals(yValue) ) {
				// If values don't implement IComparer but are equivalent
				result = 0;
			} else {
				// Values don't implement IComparer and are not equivalent, so compare as string values
				result = xValue.ToString().CompareTo(yValue.ToString());
			}
			// Return result
			return result;
		}

		private int CompareDescending(object xValue, object yValue)
		{
			// Return result adjusted for ascending or descending sort order ie
			// multiplied by 1 for ascending or -1 for descending
			return CompareAscending(xValue, yValue) * -1;
		}

		private object GetPropertyValue(T value, string property)
		{
			// Get property
			PropertyInfo propertyInfo = value.GetType().GetProperty(property);

			// Return value
			return propertyInfo.GetValue(value, null);
		}

		#endregion Private Methods

		#endregion Private Members
	}
}